import random
import config
import time

class car:

    averageMaxSpeed = config.carAverageMaxSpeed
    sigmaMaxSpeed   =  config.carSigmaMaxSpeed
    averageAcc = config.carAverageAcc
    sigmaAcc = config.carSigmaAcc
    minSpeed     = config.carMinSpeed
    
    def isInCar(self, x, y):
        cx, cy = self.getXY()
        return (cx - x) ** 2 + (cy - y) ** 2 <= self.radius ** 2  
                       
    def __init__(self, edge, edgePosition,speed, route, node, maxSpeed = None):
        self.whenWillItMakeReservation = 0
        self.position = edgePosition
        self.edge = edge
        self.speed = speed
        self.route = route
        self.remainingRoute = route.copy()
        self.edge.addCar(self)
        self.ID = str( config.carSerialNumber )
        config.carSerialNumber += 1
        self.end = False
        self.radius = config.carRadius
        self.madeReservation = False
        self.lastSafeNode = node
        self.jumpedIntoTheQueue = set()
        self.time = time.time()
        self.route.wjechalo += 1
        
        if maxSpeed is not None:
            self.acceleration = maxSpeed / 50
            self.maxSpeed = maxSpeed
        else:
            self.maxSpeed = random.gauss(car.averageMaxSpeed, car.sigmaMaxSpeed)
            while self.maxSpeed <= 0 and self.maxSpeed >= car.sigmaMaxSpeed * 4:
                self.maxSpeed = random.gauss(car.averageMaxSpeed, car.sigmaMaxSpeed)
            self.acceleration = self.maxSpeed / 50

        assert self.maxSpeed > 0
        #self.minSpeed = minSpeed
        
    def __repr__(self):
        returnValue = "car("
        returnValue += self.ID + ","
        returnValue += str(self.position) + ","
        returnValue += self.edge.ID + ","
        returnValue += self.route.ID + ","
        returnValue += str(self.speed) + ")"
        
        return returnValue
     
    def wantsToReserve(self):   
        if random.random() <= config.carProbabilityOfStop :
            self.whenWillItMakeReservation = config.carStopTime
            
    def getXY(self):
        part = 1- self.position / self.edge.length()
        x = part * self.edge.startNode.x +  (1 - part) * self.edge.endNode.x 
        y = part * self.edge.startNode.y +  (1 - part) * self.edge.endNode.y
        return (x,y) 


    def nextFewNodes(self,time):
        distance = self.distance(time) + self.position
        remainingLengths = map(lambda x: x.length(), self.remainingRoute)
        returnValue = []
        remainingNodes = self.remainingNodes()
        hasBeenInSafeNode = False
        while len(remainingLengths) > 0 and ( distance > remainingLengths[0] or not hasBeenInSafeNode):
            returnValue.append( remainingNodes[0] )
            hasBeenInSafeNode = hasBeenInSafeNode or remainingNodes[0].isSafeNode
            distance -= remainingLengths[0]
            del remainingLengths[0]
            del remainingNodes[0]
        
        if( len(remainingLengths) > 0 and len(returnValue) == 0 ):
            returnValue.append( remainingNodes[0] )
        return returnValue

    """        
    def nextStrictNodes(self,number):
        val = self.remainingNodes()[:number]
        return val
    """         
         
    def nextNodesTillSafeNode(self):
        remainingNodes = self.remainingNodes()
        returnValue = []
         
        while len(remainingNodes) > 0 and not remainingNodes[0].isSafeNode:
            returnValue.append( remainingNodes[0] )
            del remainingNodes[0]
        
        if len(remainingNodes) > 0:
            returnValue.append( remainingNodes[0] )        
            
        return returnValue
        
          
    def distance(self, time):
        return 2 * time * self.speed
        
          
    def remainingNodes(self):
        returnValue = []
        for edge in self.remainingRoute:
            returnValue += [edge.endNode]
        return returnValue
          
        
    def checkColision(self, mult):
        assert self.speed > 0
        assert self.maxSpeed > 0
        assert self.speed > 0
        assert self.maxSpeed > 0
        nextEdge = self.remainingRoute[0]
        position = self.position
        speed = mult * self.speed

        returnedSpeed, collision = nextEdge.checkColision(position,speed,self)
        if collision:
            return returnedSpeed
        
        if len(self.remainingRoute ) == 1:
            return mult * self.speed
        
        i = 1
        speed = position + speed - self.edge. length() + 1
        accSpeed = self.edge.length() - position
        nextEdge = self.remainingRoute[i]
        position = -1
        
        while True:
            returnedSpeed, collision = nextEdge.checkColision(position,speed,self)
            
            if collision:
                return accSpeed + returnedSpeed - 1
            if speed - 1 < nextEdge.length():
                return mult * self.speed
            if i == len(self.remainingRoute) - 1:
                return mult  * self.speed

            speed -= nextEdge.length()
            accSpeed += nextEdge.length()
            i += 1
            nextEdge = self.remainingRoute[i]
            
                
    def isAfterSafeNode(self,node):
        if self.isInSafeNode():
            return True
        for nextNode in self.remainingNodes():
            if nextNode == node:
                return False
            if nextNode.isSafeNode:
                return True
        assert False

    def getStartNode(self):
        startNode = self.edge.startNode
        endNode = self.edge.endNode
        if self.position < startNode.radius:
            return startNode
        else:
            return None
    
    def isInSafeNode(self):
        node = self.getStartNode();
        if node is None:
            return False
        elif node.isSomeoneInside == self:
            return node.isSafeNode
        else:
            return False
  
    def updateRouteTime(self,newTime):
        delta = newTime - self.time 
        self.time = newTime
        self.route.sumaWjechanych += delta
        
    def move(self,speed):
        
        assert self.speed > 0
        assert not speed is None
        steps = config.carCollisionCheckSteps

        if speed >= self.speed * 0.8 * steps and\
            self.speed + self.acceleration <= speed:
            self.speed += self.acceleration
        elif 0.4 * steps * self.speed <= speed <= self.speed * 0.6 * steps:
            self.speed = 0.5 * self.speed
        elif 2 * self.speed <= speed <= 4 * self.speed:
            self.speed = 0.25 * self.speed
        elif self.speed * 1 / 2 <= speed <= 2 * self.speed:
            self.speed = 0.1 * self.speed
        elif speed < self.speed * 1 / 2:
            self.speed = speed / 2

            
        if self.speed >= self.maxSpeed:
            self.speed = self.maxSpeed    

             
        self.position += self.speed
        while self.position > self.edge.length():
            self.position -= self.edge.length()
            self.remainingRoute[0].removeCar(self)
            del self.remainingRoute[0]
            if len(self.remainingRoute) > 0 :
                self.edge = self.remainingRoute[0]
                self.remainingRoute[0].addCar(self)

            else:
                self.end = True
                self.position = self.edge.length()
        
        if self.speed <= self.minSpeed:
            self.speed = self.minSpeed
        
        if self.edge.endNode.isSafeNode:
            self.lastSafeNode = self.edge.endNode
        self.jumpedIntoTheQueue = set()
